﻿#include <Sys\RenderUnit\SysRenderDatagram\VERTEXRD.HPP>
#include <Render\Render_Win32\Render_Win32_D3D\RENDER_WIN32_D3D9.HPP>
#include <Sys\RenderUnit\SYSDDCLASSID.HPP>
#include <Sys\RenderUnit\SysDatagramDisposal_D3D\VERTEXDDD3D9.HPP>
#include <Sys\RenderUnit\SysDatagramDisposal_D3D\VERTEXREGISTRYDDD3D9.HPP>
#include <Sys\RenderUnit\SysDatagramDisposal_D3D\STAGEDDD3D9.HPP>
#include <d3dx9.h>

using namespace CxxlMan;
using namespace Cxxl3D;

// 頂點數量和索引數量，主要供 CTriagleDDD3D.cpp 取用
//C3D_USINT g_VertexNum = 0, g_IndexNum = 0;


// IDatagramDisposal 寄存在 IDepositStore 的資料
class CDDDeposit:public IDDDeposit
{
  LPDIRECT3DVERTEXBUFFER9 m_pVertexBuffer;
  LPDIRECT3DINDEXBUFFER9 m_pIndexBuffer;

  C3D_USINT m_VertexNum, m_IndexNum;

  // 取得實作類別，為了避免轉形太花時間
  virtual void *GetInstance()
  {
    return this;
  }

public:
  // Constructor
  CDDDeposit(LPDIRECT3DVERTEXBUFFER9 pVertexBuffer, LPDIRECT3DINDEXBUFFER9 pIndexBuffer, C3D_USINT VertexNum, C3D_USINT IndexNum)
  {
    m_pVertexBuffer = pVertexBuffer;
    m_pIndexBuffer = pIndexBuffer;
    m_VertexNum = VertexNum;
    m_IndexNum = IndexNum;
  }

  // Destructor
  virtual ~CDDDeposit()
  {
    if( m_pVertexBuffer )	  
		  m_pVertexBuffer->Release();

	  if ( m_pIndexBuffer )
			m_pIndexBuffer->Release();
  }

  LPDIRECT3DVERTEXBUFFER9 cxxlFASTCALL GetVertexBuffer()
  {
    return m_pVertexBuffer;
  }

  LPDIRECT3DINDEXBUFFER9 cxxlFASTCALL GetIndexBuffer()
  {
    return m_pIndexBuffer;
  }

  void cxxlFASTCALL Get_VertexNum_IndexNum(C3D_USINT &VertexNum, C3D_USINT &IndexNum)
  {
    VertexNum = m_VertexNum;
    IndexNum = m_IndexNum;
  }
};


template <typename VERTEXFMT>
class CVertexDD:public IVertexDD
{  
  C3D_USINT m_VertexNum, m_IndexNum;

  // 頂點數量和索引數量，主要供 CTriagleDDD3D.cpp 取用
  void cxxlFASTCALL Get_VertexNum_IndexNum(C3D_USINT &VertexNum, C3D_USINT &IndexNum)
  {
    VertexNum = m_VertexNum;
    IndexNum = m_IndexNum;
  }

  virtual void  cxxlFASTCALL CopyVertex(VERTEXFMT *pDest,const VERTEXFMT *pSrc, C3D_USINT VertexNum) = 0;
protected:
  Smart_Ptr<CDDDeposit> CDDDeposit_Ptr;
  Smart_Ptr<IVertexRD<VERTEXFMT> > IVertexRD_Ptr;
  Smart_Ptr<IVertexRegistryDDD3D9> m_IVertexRegistryDDD3D9_Ptr;

  Smart_Ptr<IStageDDD3D9> m_IStageDDD3D9_Ptr;

  virtual bool cxxlFASTCALL Exec(const Smart_Ptr<IRenderDatagramDisposalCenterD3d> &IRenderDatagramDisposalCenterD3d_Arg,
    const Smart_Ptr<IRenderDatagram> &IRenderDatagram_Arg, LPDIRECT3DDEVICE9 pDEVICE9)
  {
    // 第一次被叫用才會取得
    if(m_IVertexRegistryDDD3D9_Ptr.isNULL())
      m_IVertexRegistryDDD3D9_Ptr = Smart_Cast<IVertexRegistryDDD3D9>(IRenderDatagramDisposalCenterD3d_Arg->GetIDatagramDisposal_D3d9(IVertexRegistryD3D9_ClassID));

    // 第一次被叫用才會取得
    if(m_IStageDDD3D9_Ptr.isNULL())
      m_IStageDDD3D9_Ptr = Smart_Cast<IStageDDD3D9>(IRenderDatagramDisposalCenterD3d_Arg->GetIDatagramDisposal_D3d9(IStageD3D9_ClassID));

    m_IVertexRegistryDDD3D9_Ptr->Set(this); // 每次被叫用須設定

    Lock(IRenderDatagram_Arg);

    Smart_Ptr<DepositStore> DepositStore_Ptr = (IVertexRD<VERTEXFMT> *)IVertexRD_Ptr;
    Smart_Ptr<IDDDeposit> IDDDeposit_Ptr = GetDeposit(DepositStore_Ptr);

    if(IDDDeposit_Ptr.isNULL())
    {
      CSmart_Ptr<const tagVertex<VERTEXFMT> > Vertex_cPtr = IVertexRD_Ptr->GetVertexData();
      if(Vertex_cPtr.isNULL() == false)
      {

        C3D_USINT VertexNum, IndexNum;
        CSmart_Array<const VERTEXFMT> Vertex_Array = NULL;
        CSmart_Array<const C3D_USINT> Index_Array = NULL;
        Vertex_cPtr->GetData(VertexNum,Vertex_Array,IndexNum,Index_Array);

        if(VertexNum > 0 && Vertex_Array.isNULL() == false && IndexNum > 0 && Index_Array.isNULL() == false)
        {

          LPDIRECT3DVERTEXBUFFER9 pVertexBuffer = NULL;
          LPDIRECT3DINDEXBUFFER9 pIndexBuffer = NULL;

          int vsize = VertexNum * sizeof(*Vertex_Array);
      	  if ( D3D_OK != pDEVICE9->CreateVertexBuffer(vsize, 0, 0, D3DPOOL_MANAGED, &pVertexBuffer, NULL) )
	        {
            CDDDeposit_Ptr.Destroy(); // 不畫出
            UnLock(IRenderDatagram_Arg);
		        return true; // 不畫出
	        }
          VERTEXFMT *pVertexArray = NULL;
      	  // 取得Vertex Buffer的記憶體位置
	        pVertexBuffer->Lock(0, vsize, (void **)&pVertexArray, 0);
          CopyVertex(pVertexArray,Vertex_Array,VertexNum);
      	  pVertexBuffer->Unlock();

          int isize = IndexNum * sizeof(*Index_Array); 
	        if ( D3D_OK != pDEVICE9->CreateIndexBuffer(isize, 0, D3DFMT_INDEX16, D3DPOOL_MANAGED, &pIndexBuffer, NULL) )
          {
            pVertexBuffer->Release();
            CDDDeposit_Ptr.Destroy(); // 不畫出
            UnLock(IRenderDatagram_Arg);
            return true;  // 不畫出
          }
          C3D_USINT *pIndexArray = NULL;
          // `取得Index Buffer的記憶體位置`
          pIndexBuffer->Lock(0, isize, (void **)&pIndexArray, 0);
          memcpy(pIndexArray, Index_Array, isize);
          pIndexBuffer->Unlock();

          CDDDeposit_Ptr = new CDDDeposit(pVertexBuffer, pIndexBuffer, VertexNum, IndexNum);
          SetDeposit((CDDDeposit *)CDDDeposit_Ptr, DepositStore_Ptr);
        }
        else
          CDDDeposit_Ptr.Destroy(); // 不畫出
      }
      else
        CDDDeposit_Ptr.Destroy(); // 不畫出
    }
    else
      CDDDeposit_Ptr = (CDDDeposit *)IDDDeposit_Ptr->GetInstance();

    if(CDDDeposit_Ptr.isNULL() == false)
      CDDDeposit_Ptr->Get_VertexNum_IndexNum(m_VertexNum, m_IndexNum);

    UnLock(IRenderDatagram_Arg);

    return true;

  }
public:
  // Constructor
  CVertexDD()
    :cxxlObject(Spirit_Easy),
    CDDDeposit_Ptr(NULL,this),
    IVertexRD_Ptr(NULL,this),
    m_IVertexRegistryDDD3D9_Ptr(NULL,this),
    m_IStageDDD3D9_Ptr(NULL,this)
  {
    m_VertexNum = 0, m_IndexNum = 0;
  }

  // Destructor
  virtual ~CVertexDD(){}
};

/*************************************************************/

class CVertexDD_NC_D3D9:public CVertexDD<tagVERTEXFMT_NC>
{

  virtual void  cxxlFASTCALL CopyVertex(tagVERTEXFMT_NC *pDest,const tagVERTEXFMT_NC *pSrc, C3D_USINT VertexNum)
  {
    int i;
    for(i = 0; i < VertexNum; ++i, ++pDest, ++pSrc)
    {
      pDest->Position = pSrc->Position;
      pDest->Normal = pSrc->Normal;
      pDest->Color.r = pSrc->Color.b;
      pDest->Color.g = pSrc->Color.g;
      pDest->Color.b = pSrc->Color.r;
      pDest->Color.a = pSrc->Color.a;       
    }
  }


  virtual bool cxxlFASTCALL Exec(const Smart_Ptr<IRenderDatagramDisposalCenterD3d> &IRenderDatagramDisposalCenterD3d_Arg,
    const Smart_Ptr<IRenderDatagram> &IRenderDatagram_Arg, LPDIRECT3DDEVICE9 pDEVICE9)
  {
    IVertexRD_Ptr = (IVertexRD_NC*)GetDefaultInterface(IRenderDatagram_Arg);
    CVertexDD<tagVERTEXFMT_NC>::Exec(IRenderDatagramDisposalCenterD3d_Arg,IRenderDatagram_Arg,pDEVICE9);

    if(CDDDeposit_Ptr.isNULL())
      return true; // 不畫出


    // 設定資料格式
    // D3DFVF_XYZ = 使用3個浮點數來記錄位置
    // D3DFVF_NORMAL
    // D3DFVF_DIFFUSE = 使用32bits整數型態來記錄BGRA顏色
	  pDEVICE9->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE); 
    

  	pDEVICE9->SetStreamSource(0, CDDDeposit_Ptr->GetVertexBuffer(), 0, sizeof(tagVERTEXFMT_NC));
	  pDEVICE9->SetIndices(CDDDeposit_Ptr->GetIndexBuffer());

  }

  // 為 Texture Stage 設定要用的 CoordIndex，主要供 CTexture2DDDD3D.cpp、CCubemapTextureDDD3D.cpp 叫用
  // 若不含 CoordIndex 回覆 false
  bool cxxlFASTCALL SetCoordIndex(LPDIRECT3DDEVICE9 pDEVICE9)
  {
    return false;
  }

public:
  // Constructor
  CVertexDD_NC_D3D9()
    :cxxlObject(Spirit_Easy)
  {
  }

  // Destructor
  ~CVertexDD_NC_D3D9()
  {
  }

};

cxxlObject *cxxlFASTCALL New_CVertexDD_NC_D3D9()
{
  return new CVertexDD_NC_D3D9;
}

/****************************************************/

class CVertexDD_C_D3D9:public CVertexDD<tagVERTEXFMT_C>
{
  virtual void  cxxlFASTCALL CopyVertex(tagVERTEXFMT_C *pDest,const tagVERTEXFMT_C *pSrc, C3D_USINT VertexNum)
  {
    // memcpy(pDest, pSrc, sizeof(tagVERTEXFMT_C)*VertexNum); // 若不做改變用這就行了
    int i;
    for(i = 0; i < VertexNum; ++i, ++pDest, ++pSrc)
    {
      pDest->Position = pSrc->Position;
      pDest->Color.r = pSrc->Color.b;
      pDest->Color.g = pSrc->Color.g;
      pDest->Color.b = pSrc->Color.r;
      pDest->Color.a = pSrc->Color.a;       
    }
  }

  virtual bool cxxlFASTCALL Exec(const Smart_Ptr<IRenderDatagramDisposalCenterD3d> &IRenderDatagramDisposalCenterD3d_Arg,
    const Smart_Ptr<IRenderDatagram> &IRenderDatagram_Arg, LPDIRECT3DDEVICE9 pDEVICE9)
  {
    IVertexRD_Ptr = (IVertexRD_C*)GetDefaultInterface(IRenderDatagram_Arg);
    CVertexDD<tagVERTEXFMT_C>::Exec(IRenderDatagramDisposalCenterD3d_Arg,IRenderDatagram_Arg,pDEVICE9);

    if(CDDDeposit_Ptr.isNULL())
      return true; // 不畫出

    // 設定資料格式
    // D3DFVF_XYZ = 使用3個浮點數來記錄位置
    // D3DFVF_DIFFUSE = 使用32bits整數型態來記錄BGRA顏色
	  pDEVICE9->SetFVF(D3DFVF_XYZ |  D3DFVF_DIFFUSE); 
    

  	pDEVICE9->SetStreamSource(0, CDDDeposit_Ptr->GetVertexBuffer(), 0, sizeof(tagVERTEXFMT_C));
	  pDEVICE9->SetIndices(CDDDeposit_Ptr->GetIndexBuffer());

  }

  // 為 Texture Stage 設定要用的 CoordIndex，主要供 CTexture2DDDD3D.cpp、CCubemapTextureDDD3D.cpp 叫用
  // 若不含 CoordIndex 回覆 false
  bool cxxlFASTCALL SetCoordIndex(LPDIRECT3DDEVICE9 pDEVICE9)
  {
    return false;
  }


public:
  // Constructor
  CVertexDD_C_D3D9()
    :cxxlObject(Spirit_Easy)
  {
  }

  // Destructor
  ~CVertexDD_C_D3D9()
  {
  }

};

cxxlObject *cxxlFASTCALL New_CVertexDD_C_D3D9()
{
  return new CVertexDD_C_D3D9;
}

/****************************************************/

class CVertexDD_N_D3D9:public CVertexDD<tagVERTEXFMT_N>
{
  virtual void  cxxlFASTCALL CopyVertex(tagVERTEXFMT_N *pDest,const tagVERTEXFMT_N *pSrc, C3D_USINT VertexNum)
  {
    memcpy(pDest, pSrc, sizeof(tagVERTEXFMT_N)*VertexNum); // 若不做改變用這就行了
  }

  virtual bool cxxlFASTCALL Exec(const Smart_Ptr<IRenderDatagramDisposalCenterD3d> &IRenderDatagramDisposalCenterD3d_Arg,
    const Smart_Ptr<IRenderDatagram> &IRenderDatagram_Arg, LPDIRECT3DDEVICE9 pDEVICE9)
  {
    IVertexRD_Ptr = (IVertexRD_N*)GetDefaultInterface(IRenderDatagram_Arg);
    CVertexDD<tagVERTEXFMT_N>::Exec(IRenderDatagramDisposalCenterD3d_Arg,IRenderDatagram_Arg,pDEVICE9);

    if(CDDDeposit_Ptr.isNULL())
      return true; // 不畫出

    // 設定資料格式
    // D3DFVF_XYZ = 使用3個浮點數來記錄位置
    // D3DFVF_NORMAL
	  pDEVICE9->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL );
    

  	pDEVICE9->SetStreamSource(0, CDDDeposit_Ptr->GetVertexBuffer(), 0, sizeof(tagVERTEXFMT_N));
	  pDEVICE9->SetIndices(CDDDeposit_Ptr->GetIndexBuffer());

  }

  // 為 Texture Stage 設定要用的 CoordIndex，主要供 CTexture2DDDD3D.cpp、CCubemapTextureDDD3D.cpp 叫用
  // 若不含 CoordIndex 回覆 false
  bool cxxlFASTCALL SetCoordIndex(LPDIRECT3DDEVICE9 pDEVICE9)
  {
    return false;
  }

public:
  // Constructor
  CVertexDD_N_D3D9()
    :cxxlObject(Spirit_Easy)
  {
  }

  // Destructor
  ~CVertexDD_N_D3D9()
  {
  }

};

cxxlObject *cxxlFASTCALL New_CVertexDD_N_D3D9()
{
  return new CVertexDD_N_D3D9;
}

/****************************************************/

class CVertexDD_NT_D3D9:public CVertexDD<tagVERTEXFMT_NT>
{
  virtual void  cxxlFASTCALL CopyVertex(tagVERTEXFMT_NT *pDest,const tagVERTEXFMT_NT *pSrc, C3D_USINT VertexNum)
  {
    memcpy(pDest, pSrc, sizeof(tagVERTEXFMT_NT)*VertexNum); // 若不做改變用這就行了
  }

  virtual bool cxxlFASTCALL Exec(const Smart_Ptr<IRenderDatagramDisposalCenterD3d> &IRenderDatagramDisposalCenterD3d_Arg,
    const Smart_Ptr<IRenderDatagram> &IRenderDatagram_Arg, LPDIRECT3DDEVICE9 pDEVICE9)
  {
    IVertexRD_Ptr = (IVertexRD_NT*)GetDefaultInterface(IRenderDatagram_Arg);
    CVertexDD<tagVERTEXFMT_NT>::Exec(IRenderDatagramDisposalCenterD3d_Arg,IRenderDatagram_Arg,pDEVICE9);

    if(CDDDeposit_Ptr.isNULL())
      return true; // 不畫出

    // 設定資料格式
    // D3DFVF_XYZ = 使用3個浮點數來記錄位置
    // D3DFVF_NORMAL
	  pDEVICE9->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1);
    

  	pDEVICE9->SetStreamSource(0, CDDDeposit_Ptr->GetVertexBuffer(), 0, sizeof(tagVERTEXFMT_NT));
	  pDEVICE9->SetIndices(CDDDeposit_Ptr->GetIndexBuffer());

  }

  // 為 Texture Stage 設定要用的 CoordIndex，主要供 CTexture2DDDD3D.cpp、CCubemapTextureDDD3D.cpp 叫用
  // 若不含 CoordIndex 回覆 false
  bool cxxlFASTCALL SetCoordIndex(LPDIRECT3DDEVICE9 pDEVICE9)
  {       
    DWORD Stage = m_IStageDDD3D9_Ptr->GetStage();
    if(Stage == 0)
      return true;

    // 貼圖座標的來源為第 0 號貼圖座標
    pDEVICE9->SetTextureStageState(Stage, D3DTSS_TEXCOORDINDEX, 0); 

    return true;
  }


public:
  // Constructor
  CVertexDD_NT_D3D9()
    :cxxlObject(Spirit_Easy)
  {
  }

  // Destructor
  ~CVertexDD_NT_D3D9()
  {
  }

};

cxxlObject *cxxlFASTCALL New_CVertexDD_NT_D3D9()
{
  return new CVertexDD_NT_D3D9;
}

/****************************************************/

class CVertexDD_T_D3D9:public CVertexDD<tagVERTEXFMT_T>
{
  virtual void  cxxlFASTCALL CopyVertex(tagVERTEXFMT_T *pDest,const tagVERTEXFMT_T *pSrc, C3D_USINT VertexNum)
  {
    memcpy(pDest, pSrc, sizeof(tagVERTEXFMT_T)*VertexNum); // 若不做改變用這就行了
  }

  virtual bool cxxlFASTCALL Exec(const Smart_Ptr<IRenderDatagramDisposalCenterD3d> &IRenderDatagramDisposalCenterD3d_Arg,
    const Smart_Ptr<IRenderDatagram> &IRenderDatagram_Arg, LPDIRECT3DDEVICE9 pDEVICE9)
  {
    IVertexRD_Ptr = (IVertexRD_T*)GetDefaultInterface(IRenderDatagram_Arg);
    CVertexDD<tagVERTEXFMT_T>::Exec(IRenderDatagramDisposalCenterD3d_Arg,IRenderDatagram_Arg,pDEVICE9);

    if(CDDDeposit_Ptr.isNULL())
      return true; // 不畫出

    // 設定資料格式
    // D3DFVF_XYZ = 使用3個浮點數來記錄位置
	  pDEVICE9->SetFVF(D3DFVF_XYZ | D3DFVF_TEX1);
    

  	pDEVICE9->SetStreamSource(0, CDDDeposit_Ptr->GetVertexBuffer(), 0, sizeof(tagVERTEXFMT_T));
	  pDEVICE9->SetIndices(CDDDeposit_Ptr->GetIndexBuffer());

  }

  // 為 Texture Stage 設定要用的 CoordIndex，主要供 CTexture2DDDD3D.cpp、CCubemapTextureDDD3D.cpp 叫用
  // 若不含 CoordIndex 回覆 false
  bool cxxlFASTCALL SetCoordIndex(LPDIRECT3DDEVICE9 pDEVICE9)
  {
    DWORD Stage = m_IStageDDD3D9_Ptr->GetStage();
    if(Stage == 0)
      return true;

    // 貼圖座標的來源為第 0 號貼圖座標
    pDEVICE9->SetTextureStageState(Stage, D3DTSS_TEXCOORDINDEX, 0); 

    return true;
  }

public:
  // Constructor
  CVertexDD_T_D3D9()
    :cxxlObject(Spirit_Easy)
  {
  }

  // Destructor
  ~CVertexDD_T_D3D9()
  {
  }

};

cxxlObject *cxxlFASTCALL New_CVertexDD_T_D3D9()
{
  return new CVertexDD_T_D3D9;
}